package com.freesky.biz.abchina.service;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.freesky.biz.abchina.config.AbChinaLifePayConfiguration;
import com.freesky.biz.abchina.pojo.DownloadTraceRequest;
import com.freesky.biz.abchina.pojo.RefundTraceRequest;
import com.freesky.biz.abchina.pojo.RefundTraceResponse;
import com.freesky.common.core.domain.AjaxResult;
import com.freesky.common.utils.StringUtils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 缴费中心
 */
@Service
public class AbChinaLifePayService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    private AbChinaLifePayConfiguration livePayConfiguration;

    //ryd公私钥
    private PrivateKey privateKey;
    private PublicKey publicKey;
    private String publicKeyStr;

    private PublicKey bankPublicKey;
    private String bankPublicKeyStr;

    private String PREFIX_URL = "https://enjoy.abchina.com/jf-openapiweb/";

    /**
     * 加载证书
     */
    @PostConstruct
    public void init() {
        String filePath = livePayConfiguration.getPfxPath();
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            FileInputStream fis = new FileInputStream(filePath);
            char[] nPassword = null;
            if (StringUtils.isEmpty(livePayConfiguration.getKeystorePassword())) {
                nPassword = null;
            } else {
                nPassword = livePayConfiguration.getKeystorePassword().toCharArray();
            }
            ks.load(fis, nPassword);
            fis.close();

            Enumeration<String> enums = ks.aliases();
            String keyAlias = null;
            if (enums.hasMoreElements()) {
                keyAlias = (String) enums.nextElement();

            }
            System.out.println("is key entry=" + ks.isKeyEntry(keyAlias));

            PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
            java.security.cert.Certificate cert = ks.getCertificate(keyAlias);
            PublicKey pubkey = cert.getPublicKey();

            this.privateKey = prikey;
            this.publicKey = pubkey;
            this.publicKeyStr = new String(Base64.encodeBase64(publicKey.getEncoded()));
            System.out.println("publicKeyStr key=" + this.publicKeyStr);


            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            cert = (X509Certificate) cf.generateCertificate(new FileInputStream(livePayConfiguration.getCerPath()));
            PublicKey publicKey = cert.getPublicKey();
            this.bankPublicKey = publicKey;
            this.bankPublicKeyStr = new String(Base64.encodeBase64(bankPublicKey.getEncoded()));
        } catch (Exception ioe){
            ioe.printStackTrace();
        }
    }


    /**
     * 加签名
     * @param dataString
     * @return
     */
    public String signWhithsha1withrsa(String dataString) {
        String signatureString = null;
        try {
            // SHA1withRSA算法进行签名
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initSign(privateKey);
            byte[] data = dataString.getBytes("utf-8");
            byte[] dataBase= Base64.encodeBase64(data);
            // 更新用于签名的数据
            sign.update(dataBase);

            byte[] signature = sign.sign();
            signatureString = new String(Base64.encodeBase64(signature));
            logger.info("加密完成，signature is : " + signatureString);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signatureString;
    }

    /**
     * 读取cer并验证公钥签名
     * @return
     */
    public boolean verifySign(String requestBody, String signature) {
        X509Certificate cert = null;
        boolean flag = false;
        try {
            Signature verifySign = Signature.getInstance("SHA1withRSA");
            verifySign.initVerify(bankPublicKey);
            // 用于验签的数据
            verifySign.update(requestBody.getBytes("utf-8"));
            flag = verifySign.verify(com.alibaba.fastjson.util.Base64.decodeFast(signature));
            // 验签由第三方做
            logger.info("verifySign is " + flag);
        } catch (Exception e) {
            e.printStackTrace();
            logger.info(e.getMessage());
        }
        return flag;
    }

    public Map<String,String> parseContent(String requestContent) {
        Map<String, String> requestMap = new HashMap<String, String>();
        String signatureString = null;
        String requestBody = null;
        if (requestContent.contains("||")) {
            signatureString = requestContent.substring(0, requestContent.indexOf("||"));
            requestBody = requestContent.substring(signatureString.length() + 2);
        }
        logger.info("截取报文的requestBody解密前:{}", requestBody);
        logger.info("截取报文的signatureString:{}", signatureString);
        String requestBodyOfDecoded = new String(com.alibaba.fastjson.util.Base64.decodeFast(requestBody));
        //使用base64解析完成后的requestBody
        requestMap.put("requestBodyOfDecoded", requestBodyOfDecoded);
        //解析前的requestBody
        requestMap.put("requestBody", requestBody);
        //获取缴费中心传送过来的签名
        requestMap.put("signatureString", signatureString);
        return requestMap;
    }

    //退款请求
    public AjaxResult refundTrace(String traceNo,String merchantId,String amtRefund){
        try{
            //创建请求对象，用来封装请求报文
            RefundTraceRequest refundTraceRequest = new RefundTraceRequest();
            //制定请求报文格式为json格式
            refundTraceRequest.setFormat("json");
            //以当前系统时间为时间戳 格式yyyyMMddHHmmssSSS
            String stamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());

            //退款交易的序列号需由第三方提供，且必须按照约定规则上送缴费中心，规则定义如下：
            //BRIDGE前缀+当前17位时间戳timeStamp + 缴费中心流水号traceNo作为后18位
            refundTraceRequest.getMessage().getHead().setTransSeqNum("BRIDGE"+stamp+traceNo);
            //交易码 refundBill  固定值
            refundTraceRequest.getMessage().getHead().setTransCode("refundTrace");
            //交易上行下送标志 固定值
            refundTraceRequest.getMessage().getHead().setTransFlag("01");
            //时间戳 格式 yyyyMMddHHmmssSSS，
            refundTraceRequest.getMessage().getHead().setTimeStamp(stamp);
            //TODO 分行4位iGoal码，用来前置分行交易,36家分行每家分行分配一个唯一的4位iGoal码，例如北京分行为2110
            refundTraceRequest.getMessage().getHead().setBranchCode("2110");

            //缴费中心流水号   需要进行退款的交易流水，例如：JF170512171412060778
            refundTraceRequest.getMessage().getInfo().setTraceNo(traceNo);
            //商户编号  要与证书一致  缴费中心商户编号，以商户为唯一维度来下载对账单
            //String merchantId="103881104410001";
            refundTraceRequest.getMessage().getInfo().setMerchantId(merchantId);
            //退款金额，精确到小数点后两位，支持部分退款功能
            refundTraceRequest.getMessage().getInfo().setAmtRefund(amtRefund);

            String reqJson = JSON.toJSONString(refundTraceRequest);
            logger.info("加密前报文："+reqJson);

            //加签名
            String signatrue = signWhithsha1withrsa(reqJson);
            //拼接报文
            String reqStr = signatrue+"||"+ Base64.encodeBase64String(reqJson.getBytes("utf-8"));

            String reqUrl = PREFIX_URL + "bridgeTrans/refundTrace";
            logger.info("发送的请求报文："+reqStr);
            //发送签名信息获取返回签名信息
            String responseStr = HttpRequest.post(reqUrl)
                    .header(Header.USER_AGENT, "Chrome")
                    .header(Header.CONTENT_TYPE,"application/json")
                    .charset("utf8")
                    .body(reqStr)
                    .timeout(10000)
                    .execute().body();

            logger.info("发送请求的地址："+reqUrl);
            logger.info("返回的响应报文："+responseStr);
            //TODO 如下可以对返回的报文进行处理，封装到map回显到页面，也可以做其他处理
            if (responseStr.startsWith("{\"string\":")) {
                return AjaxResult.error(responseStr.substring(responseStr.indexOf(":")+2,responseStr.indexOf("}")-1));
            }else if(responseStr.contains("||")){
                //截取签名信息
                String headSub = responseStr.substring(0, responseStr.indexOf("||"));
                logger.info("获取签名的前半部分："+headSub);
                //截取加密的json信息，进行解密
                String tailSub = responseStr.substring(responseStr.indexOf("||")+2);
                logger.info("获取签名的后半部分："+tailSub);

                //对获取的信息进行验签(该方法对signWhithsha1withrsa加密和Base64Util解密的字符串可以直接进行验签)
                if(!verifySign(tailSub,headSub)){
                    return AjaxResult.error("返回签名解析失败!");
                }

                //解密返回报文
                String respJson = new String(Base64.decodeBase64(tailSub));
                logger.info("获取签名解密后："+respJson);
                //截取签名信息中的json字符串，并转化为对象
                RefundTraceResponse refundTraceResponse = JSON.parseObject(respJson, new TypeReference<RefundTraceResponse>(){});
                return AjaxResult.success(refundTraceResponse);
            } else {
               return AjaxResult.error("系统发生异常！");
            }

        } catch (Exception ioe){
            ioe.printStackTrace();
            logger.error(ioe.getMessage());
            return AjaxResult.error(ioe.getMessage());
        }
    }

    //缴费对账单下载接口
    public AjaxResult downloadTrace(String merchantId,String billDate){
        try {

            //创建发送请求对象
            DownloadTraceRequest req = new DownloadTraceRequest();
            //以系统当前时间为时间戳
            String timeStamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());

            //封装数据格式为json格式 固定
            req.setFormat("json");
            // 关联交易的交易序列号需由第三方提供，且必须按照特定规则上送缴费中心，规则定义：BRIDGE前缀+当前17位时间戳timeStamp+商户号merchantId
            req.getMessage().getHead().setTransSeqNum("BRIDGE"+timeStamp+merchantId);
            //交易码，固定
            req.getMessage().getHead().setTransCode("downloadTrace");
            //上下行标志 固定
            req.getMessage().getHead().setTransFlag("01");
            //时间戳格式 yyyyMMddHHmmssSSS，
            req.getMessage().getHead().setTimeStamp(timeStamp);

            //对账单文件格式 固定值：CSV（大写）或GZ
            req.getMessage().getInfo().setFileType("CSV");
            //编码格式 固定值：UTF-8（大写）
            req.getMessage().getInfo().setCharset("UTF-8");
            //缴费中心商户编号，以商户为唯一维度来下载对账单
            req.getMessage().getInfo().setMerchantId(merchantId);
            //例如 ：req.getMessage().getInfo().setMerchantId("103881104410001");
            try {
                //指定对账单的下载日期  跟下载的交易流水交易日期对应,格式yyyyMMdd
                req.getMessage().getInfo().setBillDate(billDate);
            } catch (Exception e) {
                logger.info("获取日期异常");
                e.printStackTrace();
            }
            //对账单类型billType有两个选项：
            //	ALL：暂不提供+
            //	SUCCESS：下载所有支付成功流水及所有退款成功流水，对应缴费状态为1、5、6、4，退款SUCCESS
            req.getMessage().getInfo().setBillType("SUCCESS");

            //将封装的请求对象转化为json格式
            String reqJson = JSON.toJSONString(req);
            logger.info("加密前发送的请求报文：" + reqJson);
            //加签名
            String signatrue = signWhithsha1withrsa(reqJson);
            //拼接发送的报文信息
            String reqStr = signatrue+"||"+ Base64.encodeBase64String(reqJson.getBytes("utf-8"));
            logger.info("发送的请求报文：" + reqStr);

            //发送签名信息获取返回签名信息
            String reqUrl = PREFIX_URL + "mchtFile/downloadTrace";
            //发送签名信息获取返回签名信息
            HttpResponse httpResponse = HttpRequest.post(reqUrl)
                    .header(Header.USER_AGENT, "Chrome")
                    .header(Header.CONTENT_TYPE,"application/json")
                    .charset("utf8")
                    .body(reqStr)
                    .timeout(10000)
                    .execute();

            // 获取返回报文
            String responseStr = httpResponse.body();
            logger.info("接收到的报文："+responseStr);
            //如果系统返回未加密异常则会返回"{"+json+"}"
            if (responseStr.startsWith("{\"string\":")) {
                return AjaxResult.error(responseStr.substring(responseStr.indexOf(":")+2,responseStr.indexOf("}")));
            } else if (responseStr.contains("||")) {
                //截取签名信息
                String headSub = responseStr.substring(0, responseStr.indexOf("||"));
                logger.warn("获取签名的前半部分："+headSub);
                //截取加密的json信息，进行解密
                String tailSub = responseStr.substring(responseStr.indexOf("||")+2);
                logger.warn("获取签名的后半部分："+tailSub);
                //将获取到的json报文进行解析，获取报文体信息
                responseStr = new String(Base64.decodeBase64(tailSub));
                logger.warn("获取签名解密后："+responseStr);

                return AjaxResult.error(responseStr);

                //返回内容为正常内容，将response消息头读取直接写入httpResponse中
            }
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.bodyStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line); // 打印每一行数据
            }
            return AjaxResult.success("操作成功!");

        } catch (Exception e){
            e.printStackTrace();
            logger.error(e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }


}
